package edu.cmu.ece.ds.droidchat;

import java.util.ArrayList;
import java.util.HashSet;

import android.app.Activity;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import edu.cmu.ece.ds.droidchat.NetworkService.LocalBinder;
import edu.cmu.ece.ds.droidchat.controller.ChatSessionMessageBaseAdapter;
import edu.cmu.ece.ds.droidchat.model.ChatMessage;
import edu.cmu.ece.ds.droidchat.model.ChatSessionInfo;
import edu.cmu.ece.ds.droidchat.model.StringConstants;
import edu.cmu.ece.ds.droidchat.model.UserInfo;
import edu.cmu.ece.ds.droidchat.util.Toolbox;
import edu.cmu.ece.ds.droidchat.view.InviteUserMenuLayout;

public class ChatWindowActivity extends Activity implements TextWatcher {
    private final String TAG = "ChatWindowActivity";

    private final static int ARRAY_SIZE = 100;
    private final static int DIVIDER_HEIGHT = 4;

    private static ArrayList<ChatMessage> messageArray;
    private static ChatSessionMessageBaseAdapter messageAdapter;
    private RelativeLayout mLayout;
    private InviteUserMenuLayout menuLayout;
    private EditText mMessageToSend;
    private Button sendButton;
    private TextView mInfoText;
    private TextView mTitleText;
    private TextView mBackButton;

    private ServiceMessageReceiver mReceiver; // the BroadcastReceiver component

    private NetworkService mService;
    private boolean mBound = false;

    private String inviteeUsername;
    private boolean getPubKeySent = false;

    // I'm a ChatWindowActivity, but which chat session am I for???
    private String chatSessionID = null;
    private ChatSessionInfo chInfo = null;

    // private ChatDataSQLHelper chatData;

    private Dialog d;

    // The action listener for the EditText widget, to listen for the return key
    private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() {
        public boolean onEditorAction(TextView view, int actionId,
                KeyEvent event) {
            // If the action is a key-up event on the return key, send the
            // message
            if (actionId == EditorInfo.IME_NULL
                    && event.getAction() == KeyEvent.ACTION_UP) {
                Log.i(TAG, "Enter Key is pressed");
            }
            return true;
        }
    };

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub

    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (!mMessageToSend.isEnabled()
                || mMessageToSend.getText().length() == 0) {
            sendButton.setEnabled(false);
        } else {
            sendButton.setEnabled(true);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d(TAG, "---onCreate---");

        Intent i = getIntent();
        String chatSessionID = i
                .getStringExtra(StringConstants.EXTRA_CHAT_SESSION_ID);
        boolean isReturn;
        if (savedInstanceState == null) {
            isReturn = i.getBooleanExtra(StringConstants.EXTRA_IS_RETURNING,
                    false);
        } else {
            isReturn = true;
        }
        boolean isInviting = i.getBooleanExtra(
                StringConstants.EXTRA_IS_INVITING, false);

        System.out.println("chatSessionID=" + chatSessionID);
        System.out.println("isInviting=" + isInviting);

        chInfo = Toolbox.getChatSession(chatSessionID);
        chInfo.setIsInviting(isInviting);

        if (chInfo.isPendingForMyInvitation()) {
            System.out.println("chat session " + chatSessionID
                    + " is still pending");
        }

        if (mBound) {
            if (chInfo.isInviting() && !chInfo.InvitationSent()) {
                System.out
                        .println("calling sendInvitation() from NetworkService "
                                + chInfo.getUsername());
                mService.sendInvitation(chInfo.getUsername(),
                        chInfo.getSessionid());
            }
        }
        if (savedInstanceState == null) {
            messageArray = new ArrayList<ChatMessage>(ARRAY_SIZE);
            messageAdapter = new ChatSessionMessageBaseAdapter(this,
                    messageArray);
        }
        this.setContentView(R.layout.chat_window_layout);

        // layout
        mLayout = (RelativeLayout) this.findViewById(R.id.message_input_layout);

        // list view
        final ListView lv = (ListView) this
                .findViewById(R.id.chat_message_list_view);
        lv.setAdapter(messageAdapter);
        lv.setTextFilterEnabled(true);
        lv.setDivider(null);
        lv.setDividerHeight(DIVIDER_HEIGHT);
        lv.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        lv.setCacheColorHint(Color.WHITE);
        // scroll to bottom when a new message arrives
        lv.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

        // title
        mTitleText = (TextView) findViewById(R.id.chat_window_title);
        mTitleText.setText(chInfo.getChatSessionName());
        // back press
        mBackButton = (TextView) findViewById(R.id.chat_window_back_button);
        mBackButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.e(TAG, "Should go back to droidchattab");
                ChatWindowActivity.this.onBackPressed();
            }
        });

        // Info line
        mInfoText = (TextView) findViewById(R.id.chat_window_info_line);
        // MessageSendingBarView
        mMessageToSend = (EditText) findViewById(R.id.message_input_field);
        mMessageToSend.addTextChangedListener(this);

        // Initialize the EditText with a listener for the "return key"
        mMessageToSend.setOnEditorActionListener(mWriteListener);

        if (chInfo.isPendingForMyInvitation()) {
            Log.d("mMessageToSend", "set to enable=false");
            mMessageToSend.setEnabled(false);
            mInfoText.setText(this.getResources().getText(R.string.waiting_for)
                    + chInfo.getUsername()
                    + this.getResources().getText(R.string.decision));
            mInfoText.setVisibility(View.VISIBLE);
        }

        sendButton = (Button) findViewById(R.id.message_send_button);
        sendButton.setEnabled(false);
        sendButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
            	if(chInfo.isDead()){
            		return;
            	}
            	
                ChatMessage newMessage = new ChatMessage(Toolbox.getMyInfo(),
                        mMessageToSend.getText().toString(), chInfo
                                .getSessionid());

                System.out.println("In ChatWindowActivity for chat session "
                        + chInfo.getSessionid());
                // do some networking operation via the NetworkService API
                mService.sendChatMessage(newMessage);

                messageArray.add(newMessage);
                messageAdapter.notifyDataSetChanged();
                mMessageToSend.setText("");
                // addMessageDisplayed(newMessage);
            }
        });

        Log.e(TAG, "registering broadcast receiver");
        mReceiver = new ServiceMessageReceiver();
        IntentFilter filter = new IntentFilter();
        // only care about broadcast "ACTION_FOR_CHAT_WINDOW_ACTIVITY"
        filter.addAction(StringConstants.ACTION_FOR_CHAT_WINDOW_ACTIVITY);
        this.registerReceiver(mReceiver, filter);

        if (isReturn) {
            Log.e(TAG, "isReturn.");

            if (chInfo.getNewMessagesCount() != 0) {
                chInfo.getNewMessages();
            }
            messageArray.clear();
            messageArray.addAll(chInfo.getMessagesDisplayed());
            messageAdapter.notifyDataSetChanged();

            if (chInfo.isPendingForMyInvitation()) {
                Log.e(TAG, "isReturn && isPendingForMyInvitation");
                Log.e(TAG,
                        "number of participants="
                                + chInfo.numberOfParticipants());
                if (chInfo.numberOfParticipants() > 0) {
                    Log.e(TAG, "enable the edit text");
                    mMessageToSend.setEnabled(true);
                }
            }
            
            if(chInfo.isDead()==true){
            	 mMessageToSend.setEnabled(false);
            	 
            	 HashSet<String> left = chInfo.getParticipantLeft();
                 if(!left.isEmpty()){
                 	Log.e(TAG + " LEFT", "left is not empty");
                 	for(String username: left){
                 		if(chInfo.isNotResponsiveParticipant(username)){
                 			// has been not responsive
                 			// you've also detected that he's left
                 			chInfo.addSystemMessageNowLeft(username);
                 			chInfo.getNewMessages();
                             messageArray.clear();
                             messageArray.addAll(chInfo.getMessagesDisplayed());
                             messageAdapter.notifyDataSetChanged();
                             chInfo.removeParticipantNonResponsive(username);
                 		}
                 	}
                 }
            }
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "---onStart---");

        // we probably need to also bind to NetworkService here for this
        // Activity

        Intent intent = new Intent(this, NetworkService.class);
        Log.d(TAG, "calling bindService()");
        Toolbox.bindService(this, intent, mConnection,
                Context.BIND_AUTO_CREATE, TAG);
    }

    public void onPause() {
        super.onStop();
        Log.d(TAG, "---onPause---");
    }

    public void onStop() {
        super.onStop();
        Log.d(TAG, "---onStop---");
        Log.d(TAG, "---calling unbindService---");

        // we probably need to also unbind to NetworkService here for this
        // Activity
        Toolbox.unbindService(this, mConnection, TAG);

        // whenever the BACK key is pressed, we kill the ChatWindowActivity
        finish();
    }

    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "---onDestroy---");
        unregisterReceiver(mReceiver);
        // chatData.close();
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to TCPService, cast the IBinder and get TCPService
            // instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            Log.d(TAG, mService.getPackageName());
            mBound = true;
            Log.d(TAG, "We've bound to NetworkService");

            if (mBound) {
                if (chInfo.isInviting() && !chInfo.InvitationSent()) {

                    /*
                     * if(getPubKeySent==false){
                     * Log.d(TAG,"calling getUserPubKey() from NetworkService: "
                     * + chInfo.getUsername());
                     * mService.getUserPubKey(chInfo.getUsername(),
                     * chInfo.getSessionid()); getPubKeySent = true; }
                     */

                    System.out
                            .println("calling sendInvitation() from NetworkService "
                                    + chInfo.getUsername());
                    mService.sendInvitation(chInfo.getUsername(),
                            chInfo.getSessionid());
                    chInfo.setInvitationSent(true);

                }
            }

            /*
             * commented out because public key ain't workin
             *
             * if(getPubKeySent==false){ Log.d(TAG,
             * "calling getUserPubKey() from NetworkService: " +
             * inviteeUsername);
             *
             * // sessionCreationHashtable is list of pending chat sessions
             * String newChatSessionID =
             * Toolbox.addNewOneOnOneChatSessionInfoByUsername(inviteeUsername,
             * mService.getLoginAttribute());
             * mService.getUserPubKey(inviteeUsername, newChatSessionID);
             * getPubKeySent = true; }
             */
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBound = false;
        }
    };

    private class ServiceMessageReceiver extends BroadcastReceiver {
        // public static final String TAG = "ServerMessageReceiver";
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "onReceive: " + intent);

            String chatSessionID = intent
                    .getStringExtra(StringConstants.EXTRA_CHAT_SESSION_ID);

            System.out.println("broadcast chat session id=" + chatSessionID);
            System.out.println("chat window chat session id="
                    + chInfo.getSessionid());
            Log.e(TAG,
                    "StringConstants.EXTRA_IS_INVITATION_RESPONSE="
                            + intent.getBooleanExtra(
                                    StringConstants.EXTRA_IS_INVITATION_RESPONSE,
                                    false));
            Log.e(TAG,
                    "StringConstants.EXTRA_IS_INVITATION_ACCEPTED="
                            + intent.getBooleanExtra(
                                    StringConstants.EXTRA_IS_INVITATION_ACCEPTED,
                                    false));

            if (chInfo.getSessionid().equals(chatSessionID)) {
                Log.d(TAG, "match with my chat session id");

                boolean invitationResponse = intent.getBooleanExtra(
                        StringConstants.EXTRA_IS_INVITATION_RESPONSE, false);

                if (invitationResponse) {
                    boolean accepted = intent
                            .getBooleanExtra(
                                    StringConstants.EXTRA_IS_INVITATION_ACCEPTED,
                                    false);
                    if (accepted) {
                        // accepted
                        Log.e(TAG, "ACCEPTED");
                        // the chat session just became ready
                        // - enable the EditText
                        System.out
                                .println("session was pending, now set to non-pending");
                        mMessageToSend.setEnabled(true);
                        mInfoText.setVisibility(View.GONE);

                        // display who is currently in the
                        // chat session besides me
                        mTitleText.setText(chInfo.getChatSessionName());
                    } else {
                        Log.e(TAG + "onReceive()", "REJECTED");
                        // the chat session just became ready
                        System.out
                                .println("session was pending, now set to non-pending");
                        if (chInfo.numberOfParticipants() == 0) {
                            Log.e(TAG + "onReceive()",
                                    "# of participants is zero -> removing chat session");
                            //Toolbox.removeChatSession(chInfo.getSessionid());
                            Toolbox.removeChatSession(chInfo.getSessionid(), true);
                            finish();
                        } else {
                            // System.out.println(chInfo.participants.keySet());
                            mMessageToSend.setEnabled(true);
                            mInfoText.setVisibility(View.GONE);
                        }
                    }
                } else {
                    // not an invitation response
                    boolean chatMessage = intent.getBooleanExtra(
                            StringConstants.EXTRA_CHAT_MESSAGE, false);

                    if (chatMessage) {
                        // a chat message
                        System.out.println("probably a chat message");
                        /*
                         * ArrayList<ChatMessage> newMessagesToShow =
                         * chInfo.getNewMessages();
                         * messageArray.addAll(newMessagesToShow);
                         * messageAdapter.notifyDataSetChanged();
                         */
                        chInfo.getNewMessages();
                        messageArray.clear();
                        messageArray.addAll(chInfo.getMessagesDisplayed());
                        messageAdapter.notifyDataSetChanged();
                    } else {
                        // not a chat message
                        // see broadcastChangeInChatSession() in NetworkService
                        boolean changeInChatSession = intent.getBooleanExtra(
                                StringConstants.EXTRA_REFRESH_LIST, false);
                        if (changeInChatSession) {
                            // could either be that participant list has changed
                            // or the chat session is gone! (bc no participant
                            // left)
                            ChatSessionInfo ch = Toolbox
                                    .getChatSession(chatSessionID);
                            
                            HashSet<String> left = chInfo.getParticipantLeft();
                            if(!left.isEmpty()){
                            	Log.e(TAG + " LEFT", "left is not empty");
                            	for(String username: left){
                            		if(chInfo.isNotResponsiveParticipant(username)){
                            			// has been not responsive
                            			// you've also detected that he's left
                            			chInfo.addSystemMessageNowLeft(username);
                            			chInfo.getNewMessages();
                                        messageArray.clear();
                                        messageArray.addAll(chInfo.getMessagesDisplayed());
                                        messageAdapter.notifyDataSetChanged();
                                        chInfo.removeParticipantNonResponsive(username);
                            		}
                            	}
                            }
                            
                            if (ch == null) {
                            	mMessageToSend.setEnabled(false);
                                finish();
                            } else {
                                if (ch.isPendingForMyInvitation() == false) {
                                    mInfoText.setVisibility(View.GONE);
                                }
                                
                            	// display who is currently in the
                            	// chat session besides me
                            	mTitleText.setText(chInfo.getChatSessionName());
                            	
                            	if(ch.isDead()){
                            		mMessageToSend.setEnabled(false);
                            	}
                            }
                        } else {
                            String logout = intent.getStringExtra("logout");
                            if (logout != null) {
                                // don't actually start LoginActivity here
                                // once ChatWindowActivity finshes,
                                // DroidChatTabActivity
                                // should also get the logout broadcast
                                finish();
                            }
                        }
                    }
                }
            } else {
                Log.d(TAG, "does not match with my chat session id");
            }

        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(StringConstants.EXTRA_IS_RETURNING, true);
    }

    /********** use Menu key *********/

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.chat_session_menu, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (menuLayout != null) {
            dismissInviteUserMenu();
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

        case R.id.invite_user_to_join:
            // how do I show the user a list of his neighbors that's not
            // already in the chat session??
            if (chInfo.getUsername() == null) {
            	if(chInfo.isDead()){
            		Toast.makeText(this, R.string.dead_chat_session,
                            Toast.LENGTH_SHORT).show();
            		return true;
            	}
            	
                if (Toolbox.retrieveAllNeighborsNotInList(chInfo).size() > 0) {
                    showInviteUserMenu();
                } else {
                    // no available invitees
                    Toast.makeText(this, R.string.no_person_to_invite,
                            Toast.LENGTH_SHORT).show();
                }
            } else {
                // don't allow the invitation
                Toast.makeText(
                        this,
                        getText(R.string.invite_once_only)
                                + chInfo.getUsername(), Toast.LENGTH_SHORT)
                        .show();
            }
            return true;

        case R.id.leave_chat_session_from_menu:
            /**
             * remotely, what needs to get done? Currently there is keepalive
             * for chat sessions that you are in
             */
        	if(chInfo.isDead()==false){
            	if (mBound) {
            		mService.sendKeepAlive(chInfo.getSessionid(), true);
            	}
        	}

            /**
             * locally, leaving chat session means removing the ChatSessionInfo
             * from the hashtable in Toolbox
             */
            //Toolbox.removeChatSession(chInfo.getSessionid());
            Toolbox.removeChatSession(chInfo.getSessionid(), true);

            // Destroy the ChatWindowActivity
            finish();
            return true;
        }
        return false;
    }

    /********** end of use Menu key *********/

    protected void showInviteUserMenu() {
        menuLayout = new InviteUserMenuLayout(mLayout, chInfo);
        final ListView l = menuLayout.getListView();
        l.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                UserInfo fullObject = (UserInfo) l.getItemAtPosition(position);

                if (mBound) {
                    if (chInfo.getUsername() != null) {
                        // don't allow the invitation
                        Toast.makeText(
                                ChatWindowActivity.this,
                                getText(R.string.invite_once_only)
                                        + chInfo.getUsername(),
                                Toast.LENGTH_SHORT).show();
                    } else {
                        // check if such chat session exists first
                        boolean exist = Toolbox.chatSessionExists(chInfo
                                .getParticipants().keySet(), fullObject
                                .getUsername());

                        if (exist == false) {
                            // allow the invitation
                            mService.sendMultiUserInvitation(
                                    fullObject.getUsername(),
                                    chInfo.getSessionid(),
                                    chInfo.getParticipants(),
                                    chInfo.getT());

                            if (chInfo.isPendingForMyInvitation()) {
                                mInfoText.setText(ChatWindowActivity.this
                                        .getResources().getText(
                                                R.string.waiting_for)
                                        + chInfo.getUsername()
                                        + ChatWindowActivity.this
                                                .getResources().getText(
                                                        R.string.decision));
                                mInfoText.setVisibility(View.VISIBLE);
                                dismissInviteUserMenu();
                            }
                        } else {
                            // don't allow the invitation
                            Toast.makeText(ChatWindowActivity.this,
                                    R.string.chat_session_existed,
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
        });
        menuLayout.popup();

    }

    private void dismissInviteUserMenu() {
        menuLayout.dismiss();
        menuLayout = null;
    }

    @Override
    public void onBackPressed() {
        if (menuLayout != null) {
            dismissInviteUserMenu();
        } else {
            super.onBackPressed();
        }
    }

}
